// Package runner 处理任务的运行和声明周期管理
package runner

import (
	"errors"
	"os"
	"os/signal"
	"time"
)

// Runner 在给定的超时时间内执行一组任务
// 并且在操作系统发送中断信号时结束这些任务
type Runner struct {
	// interrupt channel 用来接收操作系统发送的信号
	interrupt chan os.Signal

	// complete channel 用来通知任务已经完成
	complete chan error

	// timeout channel 用来通知任务已经超时
	timeout <-chan time.Time

	// tasks 用来保存任务列表
	tasks []func(int)
}

// ErrTimeout 定义一个超时错误, 会在人物执行超时时被返回
var ErrTimeout = errors.New("received timeout")

// ErrInterrupt 定义一个中断错误, 会在收到操作系统事件的时候返回
var ErrInterrupt = errors.New("received interrupt")

// New 返回一个Runner实例
func New(d time.Duration) *Runner {
	return &Runner{
		// 1个缓冲的信号通道
		interrupt: make(chan os.Signal, 1),
		// 没有缓冲的信号通道，如果没有接受者那么会阻塞
		complete: make(chan error),
		timeout:  time.After(d),
	}
}

// Add 方法用来添加任务，这个任务是一个接收int类型的ID作为参数的函数
func (r *Runner) Add(tasks ...func(int)) {
	r.tasks = append(r.tasks, tasks...)
}

// Start 方法用来开始执行任务，并监视通道事件
func (r *Runner) Start() error {

	// 我们希望接收所有中断信号
	signal.Notify(r.interrupt, os.Interrupt)

	// 异步执行任务
	go func() {
		r.complete <- r.run()
	}()

	select {
	// 当任务处理完成时该通道会返回
	case err := <-r.complete:
		return err
	// 当任务处理程序运行超时时发出信号
	case <-r.timeout:
		return ErrTimeout
	}

}

func (r *Runner) run() error {
	for id, task := range r.tasks {
		if r.gotInterrupt() {
			return ErrInterrupt
		}
		// 执行注册的任务
		task(id)
	}
	return nil
}

// gotInterrupt 检查是否接收到中断信号
func (r *Runner) gotInterrupt() bool {
	select {
	// 如果有中断信号那么返回true
	case <-r.interrupt:
		// 接收到中断信号，停止后续再接收到中断信号
		signal.Stop(r.interrupt)
		return true
	// 没有终端信号返回false，继续执行
	default:
		return false
	}
}
